package com.axonactive.yolo.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.cpr.BroadcasterFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.axonactive.yolo.domain.FlightNotification;
import com.axonactive.yolo.domain.SubscribedFlight;
import com.axonactive.yolo.domain.Task;
import com.axonactive.yolo.domain.TaskNotification;
import com.axonactive.yolo.domain.TaskUserId;
import com.axonactive.yolo.dto.FlightNotificationDTO;
import com.axonactive.yolo.dto.TaskNotificationDTO;
import com.axonactive.yolo.exception.FlightNotificationNotFoundException;
import com.axonactive.yolo.exception.TaskNotificationNotFoundException;
import com.axonactive.yolo.service.interfaces.AirlineService;
import com.axonactive.yolo.service.interfaces.FlightNotificationService;
import com.axonactive.yolo.service.interfaces.SubscribedFlightService;
import com.axonactive.yolo.service.interfaces.TaskUserIdService;
import com.axonactive.yolo.service.internal.TaskNotificationService;
import com.axonactive.yolo.util.CONSTANTS;
import com.axonactive.yolo.util.session.SessionHelper;

@Controller
public class NotificationController {

	private static final Logger LOGGER = Logger
			.getLogger(NotificationController.class);

	@Autowired
	public AirlineService airlineService;

	@Autowired
	public FlightNotificationService flightNotificationService;
	
	@Autowired
	public SubscribedFlightService subscribedFlightService;
	
	@Autowired
	public TaskNotificationService taskNotificationService;
	
	@Autowired
	public TaskUserIdService taskUserIdService;

	@RequestMapping(value = "/notification", method = RequestMethod.GET)
	@ResponseBody
	public void getCurrentDate(AtmosphereResource atmosphereResource,
			HttpSession session) throws JsonGenerationException {
		if (SessionHelper.isLoggedIn(session)) {
				this.suspend(atmosphereResource);
				Broadcaster broadcaster = BroadcasterFactory.getDefault()
						.lookup(SessionHelper.getUserId(session), true);
				broadcaster.addAtmosphereResource(atmosphereResource);
				ObjectMapper objectMapper = new ObjectMapper();
				String allNotification = null;
			try {
/*				allNotification = objectMapper
						.writeValueAsString(flightNotificationService
								.findByUserId(SessionHelper.getUserId(session)));*/
				
				List<FlightNotification> flightNotifications = flightNotificationService
						.findByUserId(SessionHelper.getUserId(session));
				List<FlightNotificationDTO> flightNotificationDTOs 
						= flightNotificationService.convertToListFlightNotificationDTO(flightNotifications);
				
				allNotification = objectMapper.writeValueAsString(flightNotificationDTOs);
				
				List<TaskNotification> taskNotifications = taskNotificationService.findByUserId(SessionHelper.getUserId(session));
				List<TaskNotificationDTO> taskNotificationDTOs = taskNotificationService.convertFromTaskNotification(taskNotifications);
				
				allNotification += CONSTANTS.SEARCH_DELIMITER + objectMapper.writeValueAsString(taskNotificationDTOs);

			} catch (IOException e) {
				LOGGER.error("Error while export Json", e);
			}
			
			broadcaster.broadcast(0 + allNotification);
		}
	}

	/**
	 * Mainly used because by the time we try to suspend, the
	 * {@link AtmosphereResource} could be yet not suspended
	 */
	private void suspend(final AtmosphereResource atmosphereResource) {
		final CountDownLatch latch = new CountDownLatch(1);

		atmosphereResource
				.addEventListener(new AtmosphereResourceEventListenerAdapter() {
					public void onSuspend(AtmosphereResourceEvent event) {
						latch.countDown();
						atmosphereResource.removeEventListener(this);
					}
				});

		atmosphereResource.suspend(-1);

		try {
			latch.await();
		} catch (InterruptedException e) {
			LOGGER.info(" Interrupted ", e);
		}
	}
	
	@RequestMapping(value = "/updade_noty", method = RequestMethod.POST)
	@ResponseBody
	public void updateNotification(@RequestBody String jsonNoty,
			HttpSession session) throws JsonGenerationException {
		if(SessionHelper.isLoggedIn(session)){
			try {
				JSONArray arrayNoty = new JSONArray(jsonNoty);
				FlightNotification flightNotification = new FlightNotification();
				for(int i=0; i<arrayNoty.length();i++){				
					if(arrayNoty.getJSONObject(i).getInt("status") == 0){
						flightNotification.setId(arrayNoty.getJSONObject(i).getInt("id"));
						flightNotification.setUserId(arrayNoty.getJSONObject(i).getInt("userId"));
						flightNotification.setFlightId(arrayNoty.getJSONObject(i).getString("flightId"));
						flightNotification.setFlightRoute(arrayNoty.getJSONObject(i).getString("flightRoute"));
						flightNotification.setFlightDepartureTime(arrayNoty.getJSONObject(i).getString("flightDepartureTime"));
						flightNotification.setOldPrice(arrayNoty.getJSONObject(i).getDouble("oldPrice"));
						flightNotification.setNewPrice(arrayNoty.getJSONObject(i).getDouble("newPrice"));
						flightNotification.setFlightChangeType(arrayNoty.getJSONObject(i).getInt("flightChangeType"));
						flightNotification.setStatus(1);
						flightNotification.setSaveTime(arrayNoty.getJSONObject(i).getString("saveTime"));
						flightNotification.setIsRead(arrayNoty.getJSONObject(i).getInt("isRead"));
						flightNotificationService.update(flightNotification);
					}
				}			
			} catch (Exception e) {
				LOGGER.error("Error when read json",e);
			}
		}
	}
	
	@RequestMapping(value = "account/update-status-all-noty", method = RequestMethod.POST)
	@ResponseBody
	public void updateStatusNoty(HttpSession session) throws JsonGenerationException {
		if(SessionHelper.isLoggedIn(session)){
			// update all flight notification status to 1
			List<FlightNotification> listFlightNotification = new ArrayList<FlightNotification>();
			listFlightNotification = flightNotificationService.findByUserId(SessionHelper.getUserId(session));
			for(int i = 0; i< listFlightNotification.size(); i++){
				listFlightNotification.get(i).setStatus(1);
				try {
					flightNotificationService.update(listFlightNotification.get(i));
				} catch (FlightNotificationNotFoundException e) {
					LOGGER.error("Error when update Notification");
				}
			}
			
			// update all task notification status to 1
			List<TaskNotification> taskNotifications = taskNotificationService.findByUserId(SessionHelper.getUserId(session));
			
			for (TaskNotification taskNotification : taskNotifications) {
				taskNotification.setStatus(1);
				
				try {
					taskNotificationService.update(taskNotification);
				} catch (TaskNotificationNotFoundException e) {
					LOGGER.error("Error when update task notification status");
				}
			}
		}
	}
	
	@RequestMapping(value = "account/clear-noty", method = RequestMethod.POST)
	@ResponseBody
	public void clearNotification(HttpSession session){
		if(SessionHelper.isLoggedIn(session)){
			// delete all flight notification
			try {
				flightNotificationService.deleteAllByUserId(SessionHelper.getUserId(session));
			} catch (FlightNotificationNotFoundException e) {
				LOGGER.error("Error when delete all flight notification",e);
			}
			
			//delete all task notification
			List<TaskNotification> taskNotifications = taskNotificationService.findByUserId(SessionHelper.getUserId(session));
			
			for (TaskNotification taskNotification : taskNotifications) {
				try {
					taskNotificationService.delete(taskNotification.getId());
				} catch (TaskNotificationNotFoundException e) {
					LOGGER.error("Error when delete all task notification",e);
				}
			}
			
		}

	}
	
	
	@RequestMapping(value = "account/delete-noty", method = RequestMethod.POST)
	@ResponseBody
	public void deleteNotification(@RequestBody String idNotification,
			HttpSession session){
		if(SessionHelper.isLoggedIn(session)){
			try {
				flightNotificationService.delete(Integer.parseInt(idNotification));
			} catch (FlightNotificationNotFoundException e) {
				LOGGER.error("Error when delete notificaiton",e);
			}
		}
	}
	
	@RequestMapping(value = "account/delete-noty-task", method = RequestMethod.POST)
	@ResponseBody
	public void deleteTaskNotification(@RequestBody String idNotification,
			HttpSession session){
		if(SessionHelper.isLoggedIn(session)){
			try {
				taskNotificationService.delete(Integer.parseInt(idNotification));
			} catch (TaskNotificationNotFoundException e) {
				LOGGER.error("Error when delete task notificaiton",e);
			}
		}
	}
	
	@RequestMapping(value = "account/mark-noty-read", method = RequestMethod.POST)
	@ResponseBody
	public void markOneNotyAsRead(@RequestBody String idNotification,
			HttpSession session) throws JsonGenerationException {
		if(SessionHelper.isLoggedIn(session)){
			try {
				FlightNotification flightNotification = flightNotificationService.findById(Integer.parseInt(idNotification));
				flightNotification.setIsRead(1);
				flightNotification.setStatus(1);
				flightNotificationService.update(flightNotification);
			} catch (FlightNotificationNotFoundException e) {
				LOGGER.error("Error when update Notification",e);
			}
		}
	}
	
	@RequestMapping(value = "account/mark-noty-read-task", method = RequestMethod.POST)
	@ResponseBody
	public void markOneTaskNotyAsRead(@RequestBody String idNotification,
			HttpSession session) throws JsonGenerationException {
		if(SessionHelper.isLoggedIn(session)){
			try {
				TaskNotification taskNotification = new TaskNotification();
				taskNotification = taskNotificationService.findById(Integer.parseInt(idNotification));
				taskNotification.setIsRead(1);
				taskNotification.setStatus(1);
				taskNotificationService.update(taskNotification);
			} catch (TaskNotificationNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	
	@RequestMapping(value = "account/mark-all-noty", method = RequestMethod.POST)
	@ResponseBody
	public void markAllNotyAsRead(HttpSession session) throws JsonGenerationException {
		if(SessionHelper.isLoggedIn(session)){
			// mark all flight notification
			List<FlightNotification> listFlightNotification = new ArrayList<FlightNotification>();
			listFlightNotification = flightNotificationService.findByUserId(SessionHelper.getUserId(session));
			for(int i = 0; i< listFlightNotification.size(); i++){
				listFlightNotification.get(i).setIsRead(1);
				listFlightNotification.get(i).setStatus(1);
				try {
					flightNotificationService.update(listFlightNotification.get(i));
				} catch (FlightNotificationNotFoundException e) {
					LOGGER.error("Error when update Notification");
				}
			}
			
			// mark all task notification
			List<TaskNotification> taskNotifications = new ArrayList<>();
			taskNotifications = taskNotificationService.findByUserId(SessionHelper.getUserId(session));
			
			for (TaskNotification taskNotification : taskNotifications) {
				taskNotification.setIsRead(1);
				taskNotification.setStatus(1);
				
				try {
					taskNotificationService.update(taskNotification);
				} catch (TaskNotificationNotFoundException e) {
					LOGGER.error("Error when update Task Notification");
				}
			}
		}
	}
	
	@RequestMapping(value = "account/check-subscribe-flight-exist", method = RequestMethod.POST)
	@ResponseBody
	public String checkSubscribeFlightExist(@RequestBody String subscribeFlight, HttpSession session){
		if(SessionHelper.isLoggedIn(session)){
			SubscribedFlight subscribedFlight = subscribedFlightService.findByFlightId(subscribeFlight);
			if(subscribedFlight != null){
				return "exist";
			}else{
				return null;
			}
		}
		return null;
	}
	
	@RequestMapping(value = "account/check-task-exist", method = RequestMethod.POST)
	@ResponseBody
	public String checkTaskExist(@RequestBody String taskId, HttpSession session){
		if(SessionHelper.isLoggedIn(session)){
			TaskUserId taskUserId = taskUserIdService.findByUserIdAndTaskId(SessionHelper.getUserId(session), taskId);
			
			if (taskUserId != null) {
				return "exits";
			}
			else {
				return null;
			}
		}
		return null;
	}
	
	@RequestMapping(value = "account/notification-center", method = RequestMethod.GET)
	public ModelAndView notificationCenter(HttpSession session){
		if(SessionHelper.isLoggedIn(session)){
			ModelAndView notificationCenterView = new ModelAndView("account/notificationCenter");
			try {
				// get all flight notification
				List<FlightNotification> listFlightNotification = new ArrayList<FlightNotification>();
				listFlightNotification = flightNotificationService.findByUserId(SessionHelper.getUserId(session));
				Collections.reverse(listFlightNotification);
				notificationCenterView = new ModelAndView("account/notificationCenter");
				notificationCenterView.addObject("notifications",flightNotificationService.convertToListFlightNotificationDTO(listFlightNotification));
				
				// get all task notification
				List<TaskNotification> taskNotifications = new ArrayList<>();
				taskNotifications = taskNotificationService.findByUserId(SessionHelper.getUserId(session));
				Collections.reverse(taskNotifications);
				notificationCenterView.addObject("taskNotifications", taskNotificationService.convertFromTaskNotification(taskNotifications));
			} catch (Exception e) {
				LOGGER.error("Error when send notification to client",e);
			}		
			return notificationCenterView;
		}else{
			SessionHelper.setGoBackNoty(session, true);
			return new ModelAndView("account/login");
		}

	}
	
}
